"""
认证相关工具函数
"""
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext
import uuid
import re
import os
import hashlib
import time
from fastapi import HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session

from .config import get_settings
from models.database import User

# 密码加密上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# JWT配置
settings = get_settings()

# 登录失败记录
login_attempts = {}

def verify_password(plain_password: str, hashed_password: str, client_hashed: bool = False) -> bool:
    """验证密码（支持双重哈希）"""
    try:
        if client_hashed:
            # 客户端已经进行了SHA-256哈希，再进行一次SHA-256哈希
            double_hashed = hashlib.sha256(plain_password.encode()).hexdigest()
            return double_hashed == hashed_password
        else:
            # 客户端未哈希，进行双重SHA-256哈希
            first_hash = hashlib.sha256(plain_password.encode()).hexdigest()
            double_hash = hashlib.sha256(first_hash.encode()).hexdigest()
            return double_hash == hashed_password
    except Exception as e:
        # 如果所有验证方法都失败，返回False
        return False


def get_password_hash(password: str) -> str:
    """生成密码哈希（双重哈希：SHA-256 + SHA-256）"""
    # 先进行SHA-256哈希
    sha256_hash = hashlib.sha256(password.encode()).hexdigest()
    # 再进行一次SHA-256哈希（双重哈希）
    double_hash = hashlib.sha256(sha256_hash.encode()).hexdigest()
    return double_hash


def check_login_attempts(username: str, ip_address: str = None) -> bool:
    """检查登录尝试次数"""
    key = f"{username}:{ip_address}" if ip_address else username
    current_time = time.time()
    
    # 清理过期的记录（30分钟前）
    login_attempts[key] = [
        attempt for attempt in login_attempts.get(key, [])
        if current_time - attempt < 1800  # 30分钟
    ]
    
    # 检查最近5分钟内的失败次数
    recent_attempts = [
        attempt for attempt in login_attempts.get(key, [])
        if current_time - attempt < 300  # 5分钟
    ]
    
    return len(recent_attempts) < 5  # 5分钟内最多5次尝试


def record_login_attempt(username: str, ip_address: str = None):
    """记录登录失败尝试"""
    key = f"{username}:{ip_address}" if ip_address else username
    current_time = time.time()
    
    if key not in login_attempts:
        login_attempts[key] = []
    
    login_attempts[key].append(current_time)


def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
    """创建访问令牌"""
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now() + expires_delta
    else:
        expire = datetime.now() + timedelta(seconds=settings.jwt_expire_seconds)
    
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, settings.jwt_secret_key, algorithm=settings.jwt_algorithm)
    return encoded_jwt


def verify_token(token: str) -> Optional[dict]:
    """验证令牌"""
    try:
        payload = jwt.decode(token, settings.jwt_secret_key, algorithms=[settings.jwt_algorithm])
        return payload
    except JWTError:
        return None


def generate_task_id() -> str:
    """生成任务ID"""
    return f"task_{uuid.uuid4().hex[:16]}"


def generate_slice_id() -> str:
    """生成切片ID"""
    return f"slice_{uuid.uuid4().hex[:16]}"


def validate_email(email: str) -> bool:
    """验证邮箱格式"""
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(pattern, email) is not None


def validate_username(username: str) -> bool:
    """验证用户名格式"""
    pattern = r'^[a-zA-Z0-9_]{3,50}$'
    return re.match(pattern, username) is not None


def validate_password(password: str) -> tuple[bool, list[str]]:
    """验证密码强度，返回是否有效和错误消息列表"""
    errors = []
    
    if len(password) < 8:
        errors.append("密码长度至少8位")
    
    if not any(c.isupper() for c in password):
        errors.append("需要包含大写字母")
    
    if not any(c.islower() for c in password):
        errors.append("需要包含小写字母")
    
    if not any(c.isdigit() for c in password):
        errors.append("需要包含数字")
    
    if not any(c in '!@#$%^&*(),.?":{}|<>' for c in password):
        errors.append("需要包含特殊字符")
    
    # 检查常见弱密码
    common_passwords = ['password', '12345678', 'qwerty', 'abc123', 'password123']
    if password.lower() in common_passwords:
        errors.append("密码过于常见")
    
    return len(errors) == 0, errors


def sanitize_filename(filename: str) -> str:
    """清理文件名"""
    # 移除或替换危险字符
    filename = re.sub(r'[<>:"/\\|?*]', '_', filename)
    # 移除首尾空格
    filename = filename.strip()
    # 限制文件名长度
    if len(filename) > 255:
        name, ext = os.path.splitext(filename)
        filename = name[:255-len(ext)] + ext
    
    return filename


def format_file_size(size_bytes: int) -> str:
    """格式化文件大小"""
    if size_bytes == 0:
        return "0 B"
    
    size_names = ["B", "KB", "MB", "GB", "TB"]
    i = 0
    while size_bytes >= 1024 and i < len(size_names) - 1:
        size_bytes /= 1024.0
        i += 1
    
    return f"{size_bytes:.1f} {size_names[i]}"


def is_allowed_file(filename: str, allowed_extensions: set) -> bool:
    """检查文件是否允许"""
    return os.path.splitext(filename)[1].lower() in allowed_extensions


def get_current_user_from_token(token: str, db: Session) -> User:
    """从令牌获取当前用户"""
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    
    try:
        # 解码JWT令牌
        payload = jwt.decode(token, settings.jwt_secret_key, algorithms=[settings.jwt_algorithm])
        
        # 获取用户ID和用户名
        user_id = payload.get("sub")
        username = payload.get("username")
        
        user = None
        
        if user_id:
            # 优先通过用户ID查找
            try:
                user_id_int = int(user_id)
                user = db.query(User).filter(User.id == user_id_int).first()
            except (ValueError, TypeError):
                pass
        
        # 如果通过ID没找到，尝试通过用户名查找
        if user is None and username:
            user = db.query(User).filter(User.username == username).first()
        
        if user is None:
            raise credentials_exception
            
    except JWTError:
        raise credentials_exception
    
    if user is None:
        raise credentials_exception
    
    return user